package de.ovgu.cide.typing.jdt;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.ArrayType;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.ConstructorInvocation;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.ImportDeclaration;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.ParameterizedType;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.QualifiedType;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SimpleType;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
import org.eclipse.jdt.core.dom.SuperMethodInvocation;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.VariableDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.WildcardType;
import cide.gast.IASTNode;
import de.ovgu.cide.features.source.ColoredSourceFile;
import de.ovgu.cide.language.jdt.ASTBridge;
import de.ovgu.cide.typing.jdt.checks.FieldAccessCheck;
import de.ovgu.cide.typing.jdt.checks.FieldAssignmentCheck;
import de.ovgu.cide.typing.jdt.checks.ImportTargetCheck;
import de.ovgu.cide.typing.jdt.checks.InheritedMethodExceptionCheck;
import de.ovgu.cide.typing.jdt.checks.InheritedMethodCheck;
import de.ovgu.cide.typing.jdt.checks.LocalVariableReferenceCheck;
import de.ovgu.cide.typing.jdt.checks.MethodImplementationExceptionCheck;
import de.ovgu.cide.typing.jdt.checks.MethodImplementationNameCheck;
import de.ovgu.cide.typing.jdt.checks.MethodImplementationParameterCheck;
import de.ovgu.cide.typing.jdt.checks.MethodInvocationCheck;
import de.ovgu.cide.typing.jdt.checks.TypeImportedCheck;
import de.ovgu.cide.typing.jdt.checks.TypeReferenceCheck;
import de.ovgu.cide.typing.jdt.checks.util.CheckUtils;
import de.ovgu.cide.typing.jdt.checks.util.MethodPathItem;
import de.ovgu.cide.typing.model.ITypingCheck;
/**
* visitor for an eclipse AST that generates all kinds of checks for that
* specific file
*
* split into subclasses for different concerns
*
* TODO: several other
* checks from ASE paper maybe missing
*
* @author cKaestner, aDreiling
*
*/
class JDTCheckGenerator extends JDTCheckGenerator_TypeReferences {
public JDTCheckGenerator(ColoredSourceFile file,
JDTTypingProvider jdtTypingProvider, Set<ITypingCheck> checks) {
super(file, jdtTypingProvider, checks);
}
}
class JDTCheckGenerator_TypeReferences extends JDTCheckGenerator_LocalVariables {
public JDTCheckGenerator_TypeReferences(ColoredSourceFile file,
JDTTypingProvider jdtTypingProvider, Set<ITypingCheck> checks) {
super(file, jdtTypingProvider, checks);
}
@Override
public void visitType(Type node) {
ITypeBinding binding = node.resolveBinding();
if (binding != null)
checks.add(new TypeReferenceCheck(file, jdtTypingProvider,
bridge(node), binding));
super.visitType(node);
}
}
class JDTCheckGenerator_LocalVariables extends JDTCheckGenerator_Imports {
public JDTCheckGenerator_LocalVariables(ColoredSourceFile file,
JDTTypingProvider jdtTypingProvider, Set<ITypingCheck> checks) {
super(file, jdtTypingProvider, checks);
}
private final HashMap<IVariableBinding, IASTNode> knownVariableDeclarations = new HashMap<IVariableBinding, IASTNode>();
@Override
public boolean visit(VariableDeclarationFragment node) {
visitVD(node);
return super.visit(node);
}
@Override
public boolean visit(SingleVariableDeclaration node) {
visitVD(node);
return super.visit(node);
}
private void visitVD(VariableDeclaration node) {
IVariableBinding binding = node.resolveBinding();
if (binding != null)
knownVariableDeclarations.put(binding, bridge(node));
}
@Override
protected void visitName(Name node) {
IBinding binding = node.resolveBinding();
if (binding instanceof IVariableBinding
&& !((IVariableBinding) binding).isField()) {
if (knownVariableDeclarations.get(binding) != null)
checks
.add(new LocalVariableReferenceCheck(file,
jdtTypingProvider, bridge(node),
knownVariableDeclarations.get(binding), node
.toString()));
}
super.visitName(node);
}
}
class JDTCheckGenerator_Imports extends JDTCheckGenerator_Methods {
public JDTCheckGenerator_Imports(ColoredSourceFile file,
JDTTypingProvider jdtTypingProvider, Set<ITypingCheck> checks) {
super(file, jdtTypingProvider, checks);
}
private final HashMap<ITypeBinding, IASTNode> importedTypes = new HashMap<ITypeBinding, IASTNode>();
/**
* checks that the import declaration match the target types
*
* (import vs target types)
*/
@Override
public boolean visit(ImportDeclaration node) {
IBinding binding = node.resolveBinding();
if (binding instanceof ITypeBinding) {
importedTypes.put((ITypeBinding) binding, bridge(node));
}
if (binding != null) {
checks.add(new ImportTargetCheck(file, jdtTypingProvider,
bridge(node), binding));
}
return super.visit(node);
}
/**
* checks that imported types use the correct color for the import
* declaration
*
* (local types vs imports)
*/
@Override
public void visitType(Type node) {
ITypeBinding binding = node.resolveBinding();
if (binding != null) {
if (importedTypes.containsKey(binding)) {
checks.add(new TypeImportedCheck(file, jdtTypingProvider,
bridge(node), importedTypes.get(binding), node
.toString()));
}
}
super.visitType(node);
}
}
class JDTCheckGenerator_Methods extends JDTCheckGenerator_FieldAccess {
public JDTCheckGenerator_Methods(ColoredSourceFile file,
JDTTypingProvider jdtTypingProvider, Set<ITypingCheck> checks) {
super(file, jdtTypingProvider, checks);
}
/* METHOD INVOCATION PART */
@Override
public boolean visit(MethodInvocation node) {
IMethodBinding binding = node.resolveMethodBinding();
List args = node.arguments();
handleMethodCall(node, binding, args);
return super.visit(node);
}
@Override
public boolean visit(ConstructorInvocation node) {
IMethodBinding binding = node.resolveConstructorBinding();
List args = node.arguments();
handleMethodCall(node, binding, args);
return super.visit(node);
}
@Override
public boolean visit(ClassInstanceCreation node) {
IMethodBinding binding = node.resolveConstructorBinding();
List args = node.arguments();
handleMethodCall(node, binding, args);
return super.visit(node);
}
@Override
public boolean visit(SuperConstructorInvocation node) {
IMethodBinding binding = node.resolveConstructorBinding();
List args = node.arguments();
handleMethodCall(node, binding, args);
return super.visit(node);
}
@Override
public boolean visit(SuperMethodInvocation node) {
IMethodBinding binding = node.resolveMethodBinding();
List args = node.arguments();
handleMethodCall(node, binding, args);
return super.visit(node);
}
private void handleMethodCall(ASTNode node, IMethodBinding methodBinding, List arguments) {
if (methodBinding == null)
return;
//name check
checks.add(new MethodInvocationCheck(file, jdtTypingProvider,
bridge(node), CheckUtils.getIASTNodeList(arguments), methodBinding));
}
/* METHOD DECLARATION PART */
@Override
public boolean visit(MethodDeclaration node) {
IMethodBinding binding = node.resolveBinding();
List<MethodPathItem> inhMethods = new ArrayList<MethodPathItem>();
boolean foundInhMethods = initializeAndCollectInhData(binding,
inhMethods);
if (foundInhMethods) {
handleInheritedAbstractMethodImplementation(node, binding,
inhMethods);
handleOverridingRelationshipViolation(node, binding, inhMethods);
}
return super.visit(node);
}
private boolean initializeAndCollectInhData(IMethodBinding binding,
List<MethodPathItem> inhMethods) {
if (binding == null)
return false;
// check if method is abstract
if (Modifier.isAbstract(binding.getModifiers()))
return false;
ITypeBinding declTypeBinding = binding.getDeclaringClass();
// check only for classes
if (declTypeBinding == null || !declTypeBinding.isClass())
return false;
Set<String> checkedInterfaces = new HashSet<String>();
// (recursively) collects all keys of methods in abstract classes which
// belongs to this declaration
CheckUtils.collectSimilarMethodKeysInSuperClasses(binding,
declTypeBinding.getSuperclass(), inhMethods, checkedInterfaces);
// (recursively) collects all keys of methods in interfaces which
// belongs to this declaration
CheckUtils.collectSimilarMethodKeysInInterfaces(binding,
declTypeBinding.getInterfaces(), inhMethods, checkedInterfaces);
// the set should contain at least one inherited method
if (inhMethods.size() == 0)
return false;
return true;
}
/**
* generate checks accordingly.
*
* @param node
* @param binding
* @param declTypeBinding
*/
private void handleInheritedAbstractMethodImplementation(
MethodDeclaration node, IMethodBinding methodBinding,
List<MethodPathItem> inhMethods) {
// add check for method name
checks.add(new MethodImplementationNameCheck(file, jdtTypingProvider,
bridge(node), methodBinding, inhMethods));
//add checks for parameters
List parameterList = node.parameters();
for (int j = 0; j < parameterList.size(); j++) {
checks.add(new MethodImplementationParameterCheck(file,
jdtTypingProvider,
bridge((SingleVariableDeclaration) parameterList.get(j)),
methodBinding, j, inhMethods));
}
// add checks for exceptions
List exceptionList = node.thrownExceptions();
Name curExcNode;
for (int i = 0; i < exceptionList.size(); i++) {
curExcNode = (Name) exceptionList.get(i);
ITypeBinding curExcBinding = curExcNode.resolveTypeBinding();
if (curExcBinding == null)
continue;
checks.add(new MethodImplementationExceptionCheck(file,
jdtTypingProvider, bridge(curExcNode), methodBinding,
curExcBinding.getKey(), inhMethods));
}
}
private void handleOverridingRelationshipViolation(MethodDeclaration node,
IMethodBinding methodBinding, List<MethodPathItem> inhMethods) {
// get first overridden item
MethodPathItem superItem = CheckUtils
.getFirstNonAbstractItem(inhMethods);
if (superItem == null)
return;
// add check for method name and params
checks.add(new InheritedMethodCheck(file, jdtTypingProvider,
bridge(node), CheckUtils.getIASTNodeList(node.parameters()), methodBinding.getName(), superItem));
// get all keys of method exceptions in super classes which are cast
// compatible to exceptions of "node"
// the list should contain at least one overridden exception key
List exceptionList = node.thrownExceptions();
Name curExcNode;
for (int i = 0; i < exceptionList.size(); i++) {
curExcNode = (Name) exceptionList.get(i);
ITypeBinding curExcBinding = curExcNode.resolveTypeBinding();
if (curExcBinding == null)
continue;
checks.add(new InheritedMethodExceptionCheck(file,
jdtTypingProvider, bridge(curExcNode), curExcBinding
.getName(), superItem.getInheritedExceptionKeys(
methodBinding).get(curExcBinding.getKey())));
}
}
/* *//**
* util function
*
* returns a super method or null of there is none
*/
/*
* private static IMethod findOverriddenMethod(IMethod method) { try { IType
* type = method.getDeclaringType(); ITypeHierarchy hierarchy =
* type.newSupertypeHierarchy(null);
*
* while ((type = hierarchy.getSuperclass(type)) != null) { IMethod[]
* overriddenMethods = type.findMethods(method); if (overriddenMethods !=
* null && overriddenMethods.length > 0) return overriddenMethods[0]; }
*
* return null; } catch (JavaModelException e) { return null; } }
*/
}
class JDTCheckGenerator_FieldAccess extends JDTCheckGenerator_Base {
public JDTCheckGenerator_FieldAccess(ColoredSourceFile file,
JDTTypingProvider jdtTypingProvider, Set<ITypingCheck> checks) {
super(file, jdtTypingProvider, checks);
}
@Override
protected void visitName(Name node) {
IBinding binding = node.resolveBinding();
if (binding != null && binding instanceof IVariableBinding
&& ((IVariableBinding) binding).isField()) {
handleFieldChecks(node, (IVariableBinding) binding);
}
super.visitName(node);
}
private void handleFieldChecks(Name node, IVariableBinding binding) {
//add field access check
checks.add(new FieldAccessCheck(file, jdtTypingProvider,
bridge(node), binding));
//add assignment check for final fields
if (Modifier.isFinal((binding).getModifiers())) {
ITypeBinding declClass = binding.getDeclaringClass();
if (declClass != null && declClass.isFromSource())
checks.add(new FieldAssignmentCheck(file, jdtTypingProvider,
bridge(node), binding));
}
}
}
class JDTCheckGenerator_Base extends ASTVisitor {
protected final ColoredSourceFile file;
protected final Set<ITypingCheck> checks;
protected final JDTTypingProvider jdtTypingProvider;
public JDTCheckGenerator_Base(ColoredSourceFile file,
JDTTypingProvider jdtTypingProvider, Set<ITypingCheck> checks) {
this.file = file;
this.jdtTypingProvider = jdtTypingProvider;
this.checks = checks;
}
@Override
public boolean visit(SimpleName node) {
visitName(node);
return super.visit(node);
}
@Override
public boolean visit(QualifiedName node) {
visitName(node);
return super.visit(node);
}
protected void visitName(Name node) {
}
protected void visitType(Type node) {
}
@Override
public boolean visit(ArrayType node) {
visitType(node);
return super.visit(node);
}
@Override
public boolean visit(ParameterizedType node) {
visitType(node);
return super.visit(node);
}
@Override
public boolean visit(PrimitiveType node) {
visitType(node);
return super.visit(node);
}
@Override
public boolean visit(QualifiedType node) {
visitType(node);
return super.visit(node);
}
@Override
public boolean visit(SimpleType node) {
visitType(node);
return super.visit(node);
}
@Override
public boolean visit(WildcardType node) {
visitType(node);
return super.visit(node);
}
protected static IASTNode bridge(ASTNode node) {
return ASTBridge.bridge(node);
}
}